package dk.contix.ant.gwt;

import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.Iterator;

import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.types.FileSet;

import com.google.gwt.dev.util.log.PrintWriterTreeLogger;

public class GWT15Compile {
	private CompileConfig config;
	private final Project project;
	
	public GWT15Compile(CompileConfig config, Project project) {
		this.config = config;
		this.project = project;
	}

	public void execute() throws BuildException {
		try {
			// this sets up the classloading correctly.
			try {
				Thread.currentThread().getContextClassLoader().loadClass("com.google.gwt.core.client.GWT");
				URL u = Class.forName("com.google.gwt.dev.About").getClassLoader().getResource("com/google/gwt/dev/About.class");
				if (u == null || !u.getProtocol().equals("jar") || !u.getFile().startsWith("file:")) {
					throw new BuildException("gwt-dev-*.jar is missing");
				}
				String jar = u.getFile().substring(5).substring(0, u.getFile().indexOf('!') - 5);
				System.setProperty("gwt.devjar", jar);
			} catch (Throwable e1) {
				e1.printStackTrace();
				throw new BuildException("Unable to locate gwt-dev-*.jar");
			}

			ClassLoader cl = Thread.currentThread().getContextClassLoader();//new URLClassLoader(new URL[] {}, getClass().getClassLoader());

			Object compiler = cl.loadClass("com.google.gwt.dev.GWTCompiler").newInstance();

			PrintWriterTreeLogger logger = new PrintWriterTreeLogger();
			config.setLevel(logger);
			setStyle(compiler);

			for (Iterator i = config.getFilesets().iterator(); i.hasNext();) {

				FileSet fs = (FileSet) i.next();
				DirectoryScanner ds = fs.getDirectoryScanner(project);
				String[] files = ds.getIncludedFiles();
				for (int j = 0; j < files.length; j++) {

					Method out = compiler.getClass().getMethod("setOutDir", new Class[] { File.class });
					out.invoke(compiler, new Object[] { new File(config.getDestdir()) });

					if (!files[j].endsWith(".gwt.xml")) {
						throw new BuildException("File " + files[j] + " is not a GWT file (.gwt.xml)");
					}
					String module = files[j].substring(0, files[j].indexOf('.')).replace(File.separatorChar, '.');
					if ((config.isOptimize() && Util.hasModifiedFiles(ds.getBasedir(), files[j], config.getDestdir())) || !config.isOptimize()) {
						String modXml = Util.readFile(new File(ds.getBasedir(), files[j]));
						if (modXml.indexOf("<entry-point ") > -1) {
							System.out.println("Compiling " + module);

							Class loader = cl.loadClass("com.google.gwt.dev.cfg.ModuleDefLoader");

							Method m = loader.getMethod("loadFromClassPath", new Class[] { cl.loadClass("com.google.gwt.core.ext.TreeLogger"), String.class });
							Object def = m.invoke(null, new Object[] { logger, module});

							Method distill = compiler.getClass().getMethod("distill", new Class[] { cl.loadClass("com.google.gwt.core.ext.TreeLogger"), cl.loadClass("com.google.gwt.dev.cfg.ModuleDef" ) } );
							distill.invoke(compiler, new Object[] { logger, def });
						}
					} else {
						System.out.println("Skipping module " + module + ", no changes detected");
					}
				}
			}
		} catch (InvocationTargetException e) {
			throw new BuildException("Unable to compile GWT", e);
		} catch (Throwable t) {
			throw new BuildException("Unable to compile GWT", t);
		}
	}


	private void setStyle(Object compiler) throws SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {
		String method;
		String style = config.getStyle();
		if (style == null || style.equals("obfuscated")) {
			method = "setStyleObfuscated";
		} else if (style.equals("pretty")) {
			method = "setStylePretty";
		} else if (style.equals("detailed")) {
			method = "setStyleDetailed";
		} else {
			throw new BuildException("Style unknown: " + style);
		}

		compiler.getClass().getMethod(method, null).invoke(compiler, null);
	}

}
